package com.tfswx.fljdmxyy.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.newbie.core.exception.BusinessException;
import com.tfswx.fljdmxyy.dao.SjglYxStzdMapper;
import com.tfswx.fljdmxyy.dao.SjglYxTjscMapper;
import com.tfswx.fljdmxyy.dao.SjglYxYwstMapper;
import com.tfswx.fljdmxyy.dto.sjgl.*;
import com.tfswx.fljdmxyy.enums.BusinessCodeEnum;
import com.tfswx.fljdmxyy.enums.ZdlxEnum;
import com.tfswx.fljdmxyy.model.SjglYxStzd;
import com.tfswx.fljdmxyy.model.SjglYxTjsc;
import com.tfswx.fljdmxyy.model.SjglYxYwst;
import com.tfswx.fljdmxyy.query.sjgl.*;
import com.tfswx.fljdmxyy.service.SjglService;
import com.tfswx.fljdmxyy.utils.ConverterUtils;
import com.tfswx.newbie.boot.starter.page.PageHelper;
import com.tfswx.newbie.boot.starter.page.PageParam;
import com.tfswx.newbie.boot.starter.page.PageResult;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;


/**
 * 数据管理实现类
 *
 * @Author 陈文佳
 * @Date 2023/03/24
 */
@Service
public class SjglServiceImpl implements SjglService {

    @Resource
    private SjglYxYwstMapper sjglYxYwstMapper;

    @Resource
    private SjglYxStzdMapper sjglYxStzdMapper;

    @Resource
    private SjglYxTjscMapper sjglYxTjscMapper;

    @Override
    public List<YwstQueryResult> queryYwst(YwstQueryParam param) {
        return ConverterUtils.convertList(sjglYxYwstMapper.querySjglYxYwst(param), YwstQueryResult.class);
    }

    @Override
    public Boolean updateYwst(YwstUpdateInputDTO dto) {
        SjglYxYwst stbmParam = ConverterUtils.convert(dto, SjglYxYwst.class);
        stbmParam.setZhxgsj(new Date());
        int res = sjglYxYwstMapper.updateSjglYxYwst(stbmParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.XGSB);
        }
        return Boolean.TRUE;
    }

    @Override
    public TjscGetOutputDTO getTjsc(TjscGetInputDTO dto) {
        SjglYxTjsc tjscParam = ConverterUtils.convert(dto, SjglYxTjsc.class);
        SjglYxTjsc sjglYxTjsc = sjglYxTjscMapper.getSjglYxTjsc(tjscParam);
        return ConverterUtils.convert(sjglYxTjsc, TjscGetOutputDTO.class);
    }

    @Override
    public List<TjscQueryResult> queryTjsc(TjscQueryParam param) {
        return sjglYxTjscMapper.querySjglYxTjsc(param);
    }

    @Override
    public StzdAddOutputDTO addStzd(StzdAddInputDTO dto) {
        // 1.校验实体表是否存在
        SjglYxYwst stbm = ConverterUtils.convert(dto, SjglYxYwst.class);
        stbm = sjglYxYwstMapper.getSjglYxYwst(stbm);
        if (ObjectUtil.isNull(stbm)) {
            throw new BusinessException(BusinessCodeEnum.TJSJSB.getCode(), "实体表不存在，实体编号：" + dto.getStbh());
        }

        // 2.新增实体字段
        SjglYxStzd stzdParam = ConverterUtils.convert(dto, SjglYxStzd.class);
        stzdParam.setZdbh(IdUtil.fastSimpleUUID().toUpperCase());
        stzdParam.setCjsj(new Date());
        stzdParam.setZhxgsj(new Date());
        int res = sjglYxStzdMapper.insertSjglYxStzd(stzdParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.TJSJSB);
        }
        return ConverterUtils.convert(stzdParam, StzdAddOutputDTO.class);
    }

    @Override
    public StzdGetOutputDTO getStzd(StzdGetInputDTO dto) {
        SjglYxStzd stzdParam = ConverterUtils.convert(dto, SjglYxStzd.class);
        SjglYxStzd sjglYxStzd = sjglYxStzdMapper.getSjglYxStzd(stzdParam);
        return ConverterUtils.convert(sjglYxStzd, StzdGetOutputDTO.class);
    }

    @Override
    public Boolean updateTjsc(TjscUpdateInputDTO dto) {
        SjglYxTjsc tjscParam = ConverterUtils.convert(dto, SjglYxTjsc.class);
        tjscParam.setZhxgsj(new Date());
        int res = sjglYxTjscMapper.updateSjglYxTjsc(tjscParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.XGSB);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteYwst(YwstDeleteInputDTO dto) {
        // 1.删除业务实体
        SjglYxYwst stbmParam = ConverterUtils.convert(dto, SjglYxYwst.class);
        int res = sjglYxYwstMapper.deleteSjglYxYwst(stbmParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.SCSB);
        }

        // 2.删除业务实体字段（实体字段可能没有，所以不对删除成功数量进行判断）
        SjglYxStzd stzdParam = ConverterUtils.convert(dto, SjglYxStzd.class);
        sjglYxStzdMapper.deleteSjglYxStzd(stzdParam);
        return Boolean.TRUE;
    }

    @Override
    public List<StzdQueryResult> queryStzd(StzdQueryParam param) {
        return sjglYxStzdMapper.querySjglYxStzd(param);
    }

    @Override
    public Boolean deleteTjsc(TjscDeleteInputDTO dto) {
        SjglYxTjsc tjscParam = ConverterUtils.convert(dto, SjglYxTjsc.class);
        tjscParam.setZhxgsj(new Date());
        int res = sjglYxTjscMapper.deleteSjglYxTjsc(tjscParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.SCSB);
        }
        return Boolean.TRUE;
    }

    @Override
    public YwstAddOutputDTO addYwst(YwstAddInputDTO dto) {
        SjglYxYwst stbmParam = ConverterUtils.convert(dto, SjglYxYwst.class);
        stbmParam.setStbh(IdUtil.fastSimpleUUID().toUpperCase());
        Date date = new Date();
        stbmParam.setCjsj(date);
        stbmParam.setZhxgsj(date);
        int res = sjglYxYwstMapper.insertSjglYxYwst(stbmParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.TJSJSB);
        }
        return ConverterUtils.convert(stbmParam, YwstAddOutputDTO.class);
    }

    @Override
    public Boolean deleteStzd(StzdDeleteInputDTO dto) {
        SjglYxStzd stzdParam = ConverterUtils.convert(dto, SjglYxStzd.class);
        int res = sjglYxStzdMapper.deleteSjglYxStzd(stzdParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.SCSB);
        }
        return Boolean.TRUE;
    }

    @Override
    public TjscAddOutputDTO addTjsc(TjscAddInputDTO dto) {
        SjglYxTjsc tjscParam = ConverterUtils.convert(dto, SjglYxTjsc.class);
        tjscParam.setScbh(IdUtil.fastSimpleUUID().toUpperCase());
        Date date = new Date();
        tjscParam.setCjsj(date);
        tjscParam.setZhxgsj(date);
        int res = sjglYxTjscMapper.insertSjglYxTjsc(tjscParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.TJSJSB);
        }
        return ConverterUtils.convert(tjscParam, TjscAddOutputDTO.class);
    }

    @Override
    public YwstGetOutputDTO getYwst(YwstGetInputDTO dto) {
        SjglYxYwst stbmParam = ConverterUtils.convert(dto, SjglYxYwst.class);
        SjglYxYwst sjglYxYwst = sjglYxYwstMapper.getSjglYxYwst(stbmParam);
        return ConverterUtils.convert(sjglYxYwst, YwstGetOutputDTO.class);
    }

    @Override
    public Boolean updateStzd(StzdUpdateInputDTO dto) {
        SjglYxStzd stzdParam = ConverterUtils.convert(dto, SjglYxStzd.class);
        stzdParam.setZhxgsj(new Date());
        int res = sjglYxStzdMapper.updateSjglYxStzd(stzdParam);
        if (res < 1) {
            throw new BusinessException(BusinessCodeEnum.XGSB);
        }
        return Boolean.TRUE;
    }

    @Override
    public List<YwzdgxQueryResult> queryYwzdgx() {
        return sjglYxYwstMapper.queryYwzdgx();
    }

    @Override
    public PageResult<Map<String, Object>> queryData(PageParam<DataQueryParam> param) {
        if (param.getFilter().getStjh().stream().allMatch(st -> ObjectUtil.isNull(st)
                || ObjectUtil.isEmpty(st.getStbh()))) {
            return null;
        }
        // 1.转换实体参数
        this.convertInputStbm(param);
        // 2.拼装sql
        String sql = this.bindSql(param);
        return PageHelper.doSelectPage(param, () -> sjglYxYwstMapper.queryData(sql));
    }

    @Override
    public void exportData(PageParam<DataQueryParam> param, HttpServletResponse response) {
        // 1.转换实体参数
        this.convertInputStbm(param);
        // 2.拼装sql
        String sql = this.bindSql(param);
        PageResult<Map<String, Object>> pageResult = PageHelper.doSelectPage(param,
                () -> sjglYxYwstMapper.queryData(sql));
        // 3.生成Excel
        this.bindExcel(param.getFilter(), pageResult.getList(), response);
    }

    @Override
    public void exportAll(PageParam<DataQueryParam> param, HttpServletResponse response) {
        // 1.转换实体参数
        this.convertInputStbm(param);
        // 2.拼装sql
        String sql = this.bindSql(param);
        List<Map<String, Object>> mapList = sjglYxYwstMapper.queryData(sql);
        // 3.生成Excel
        this.bindExcel(param.getFilter(), mapList, response);
    }

    @Override
    public List<StxxQueryResult> queryStxx() {
        return sjglYxYwstMapper.queryStxx();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addYwstzd(YwstzdAddInputDTO dto) {
        // 1.查询所有业务实体数据
        List<SjglYxYwst> ywstList = sjglYxYwstMapper.querySjglYxYwst(new YwstQueryParam());

        // 2.添加业务实体
        dto.getStxx().forEach(stxx -> {
            // 2.1 查询库里是否有该条数据
            List<SjglYxYwst> ywsts = ywstList.stream()
                    .filter(ywst -> ywst.getStbm().equals(stxx.getStbm()))
                    .collect(Collectors.toList());
            if (CollUtil.isNotEmpty(ywsts)) {
                SjglYxYwst sjglYxYwst = ywsts.stream().findFirst().orElse(null);
                String stbh = sjglYxYwst.getStbh();

                // 2.2 查询已有业务实体字段
                StzdQueryParam stzdParam = new StzdQueryParam();
                stzdParam.setStbh(stbh);
                List<StzdQueryResult> stzdList = sjglYxStzdMapper.querySjglYxStzd(stzdParam);
                stxx.getZdxx().forEach(zdxx -> {
                    // 2.3 如果该字段不存在就新增
                    if (stzdList.stream().noneMatch(stzd -> stzd.getZdmc().equals(zdxx.getZdmc()))) {
                        StzdAddInputDTO stzdAddInputDTO = ConverterUtils.convert(zdxx, StzdAddInputDTO.class);
                        stzdAddInputDTO.setStbh(stbh);
                        stzdAddInputDTO.setZdlx(ZdlxEnum.ZFC.getCode());
                        this.addStzd(stzdAddInputDTO);
                    }
                });
            } else {
                // 2.4 添加业务实体
                YwstAddInputDTO ywstAddInputDTO = ConverterUtils.convert(stxx, YwstAddInputDTO.class);
                YwstAddOutputDTO ywstAddOutputDTO = this.addYwst(ywstAddInputDTO);

                // 2.5 添加业务实体字段
                stxx.getZdxx().forEach(zdxx -> {
                    StzdAddInputDTO stzdAddInputDTO = ConverterUtils.convert(zdxx, StzdAddInputDTO.class);
                    stzdAddInputDTO.setStbh(ywstAddOutputDTO.getStbh());
                    stzdAddInputDTO.setZdlx(ZdlxEnum.ZFC.getCode());
                    this.addStzd(stzdAddInputDTO);
                });
            }
        });

        return Boolean.TRUE;
    }

    /**
     * 生成Excel
     *
     * @param dto
     * @param mapList
     * @param response
     */
    private void bindExcel(DataQueryParam dto, List<Map<String, Object>> mapList, HttpServletResponse response) {
        try {
            // 1.生成Excel
            ExcelWriter writer = ExcelUtil.getWriter();
            // 2.设置字段和别名
            dto.getXszdjh().forEach(a -> writer.addHeaderAlias(a.getZdmc(), a.getXsmc()));
            // 3.设置只导出有别名的字段
            writer.setOnlyAlias(true);
            // 4.写入数据
            writer.write(mapList);
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                    "attachment;Filename=" + UUID.randomUUID() + ".xls");
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out);
            writer.close();
        } catch (IOException e) {
            throw new BusinessException(e);
        }
    }

    private String bindSql(PageParam<DataQueryParam> dto) {
        List<DataQueryParam.Stjh> formData = dto.getFilter().getStjh();
        StringBuilder sbSql = new StringBuilder();
        StringBuilder sbTj = new StringBuilder();
        sbSql.append("SELECT ");
        sbSql.append(dto.getFilter().getXszdjh()
                .stream()
                .map(DataQueryParam.Xszdjh::getStbh)
                .collect(Collectors.joining(",")));
        sbSql.append(" FROM ");
        for (int i = 0; i < formData.size(); i++) {
            // 1.拼装表名及关联表
            if (StringUtils.isEmpty(formData.get(i).getStgx())) {
                if (i > 0) {
                    sbSql.append(",").append(formData.get(i).getStbh());
                } else {
                    sbSql.append(formData.get(i).getStbh());
                }
            } else {
                sbSql.append(" LEFT JOIN ").append(formData.get(i).getStbh())
                        .append(" ON ").append(formData.get(i).getStgx());
            }

            // 2.拼装WHERE条件
            sbTj.append(" AND ").append(formData.get(i).getStbh()).append(".SFSC='N' ");
            for (int j = 0; j < formData.get(i).getZdjhList().size(); j++) {
                DataQueryParam.Zdjh zdjh = formData.get(i).getZdjhList().get(j);
                if (StringUtils.containsIgnoreCase(zdjh.getYsf(), "IN")) {
                    sbTj.append(" AND ").append(zdjh.getZdmc()).append(" ")
                            .append(StringEscapeUtils.unescapeXml(zdjh.getYsf())).append(" ")
                            .append(zdjh.getZdz());
                } else {
                    sbTj.append(" AND ").append(zdjh.getZdmc()).append(" ")
                            .append(StringEscapeUtils.unescapeXml(zdjh.getYsf()))
                            .append(" '").append(zdjh.getZdz()).append("'");
                }
            }
        }
        sbSql.append(" WHERE 1=1 ").append(sbTj);
        return sbSql.toString();
    }

    /**
     * 实体表名转换
     *
     * @param dto
     */
    private void convertInputStbm(PageParam<DataQueryParam> dto) {
        List<DataQueryParam.Stjh> formData = dto.getFilter().getStjh();
        List<DataQueryParam.Xszdjh> xszdjhs = dto.getFilter().getXszdjh();
        // 1.查询实体表名
        List<SjglYxYwst> stbxx = sjglYxYwstMapper.querySjglYxYwst(new YwstQueryParam());
        if (CollectionUtil.isNotEmpty(stbxx)) {
            // 2.实体编号替换实体表名
            formData.forEach(item -> {
                // 2.1 替换表名
                item.setStbh(stbxx.stream()
                        .filter(st -> st.getStbh().equals(item.getStbh()))
                        .map(SjglYxYwst::getStbm).collect(Collectors.joining()));
                if (CollUtil.isNotEmpty(item.getZdjhList())) {
                    // 2.2 替换条件中的表名
                    item.getZdjhList().forEach(zdjh -> {
                        zdjh.setZdmc(stbxx.stream()
                                .filter(st -> st.getStbh().equals(zdjh.getStbh()))
                                .map(SjglYxYwst::getStbm).findFirst().get() + "." + zdjh.getZdmc());
                    });
                }
                if (CollUtil.isNotEmpty(item.getStgxList())) {
                    // 2.3 替换表关系
                    StringBuilder stgxBuild = new StringBuilder();
                    for (int i = 0; i < item.getStgxList().size(); i++) {
                        DataQueryParam.Stgx stgx = item.getStgxList().get(i);
                        stgxBuild.append(stbxx.stream()
                                .filter(st -> st.getStbh().equals(stgx.getStbh()))
                                .map(SjglYxYwst::getStbm).findFirst().get()).append(".").append(stgx.getZdmc());
                        if (i == 0) {
                            stgxBuild.append("=");
                        }
                    }
                    item.setStgx(stgxBuild.toString());
                }
            });
            // 3.替换查询字段中的表名
            xszdjhs.forEach(item -> {
                String stbm = stbxx.stream()
                        .filter(st -> st.getStbh().equals(item.getStbh()))
                        .map(SjglYxYwst::getStbm).findFirst().get();
                String stbh = item.getStbh();
                item.setStbh(stbm + "." + item.getZdmc() + " " + item.getPjf() + stbh + item.getZdmc());
                item.setZdmc(item.getPjf() + stbh + item.getZdmc());
            });
        }
    }
}
